import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.junit.jupiter.api.Test;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.JdkRegexpMethodPointcut;
import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;

public class T08_Pointcut {

    @Test
    public void nameMatchMethodPointcutAdvisorTest() {
        ProxyFactory factory = new ProxyFactory(new Person());
        // 声明一个通知（此处使用环绕通知 MethodInterceptor ）
        Advice advice = (MethodInterceptor) invocation -> {
            System.out.println("============>放行前拦截...");
            Object obj = invocation.proceed();
            System.out.println("============>放行后拦截...");
            return obj;
        };
        // 声明一个aspectj切点,一张切面
        // NameMatchMethodPointcut cut = new NameMatchMethodPointcut();
        // cut.setMappedName("run"); // 会匹配所有的方法名为 run 的方法
        // 切点+通知
        // Advisor advisor = new DefaultPointcutAdvisor(cut, advice);

        NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor();
        advisor.setMappedName("run");
        advisor.setAdvice(advice);

        factory.addAdvisor(advisor);
        Person p = (Person) factory.getProxy();

        // 执行方法
        p.run();
        p.run(10);
        p.say();
        p.sayHi("Jack");
        p.say("Tom", 666);
    }

    @Test
    public void expressionPointcutTest() {
        String pointcutExpression = "execution(int aop.PointcutTest.Person.run())"; // 会拦截 Person.run() 方法
        // String pointcutExpression = "args()"; // 所有没有入参的方法会被拦截。  比如：run() 会拦截，但是 run(int i) 不会被拦截
        // AspectJExpressionPointcut 支持的表达式一共有 11 种（也就是 Spring 全部支持的切点表达式类型）
        // String pointcutExpression = "@annotation(org.springframework.test.context.transaction.AfterTransaction)"; // 拦截标有 @AfterTransaction 此注解的任意方法们

        // =============================================================
        ProxyFactory factory = new ProxyFactory(new Person());

        // 声明一个 aspectj 切点，一张切面
        AspectJExpressionPointcut cut = new AspectJExpressionPointcut();
        cut.setExpression(pointcutExpression); // 设置切点表达式

        // 声明一个通知（此处使用环绕通知 MethodInterceptor ）
        Advice advice = (MethodInterceptor) invocation -> {
            System.out.println("============>放行前拦截...");
            Object obj = invocation.proceed();
            System.out.println("============>放行后拦截...");
            return obj;
        };

        // 切面 = 切点 + 通知
        // 它还有个构造函数：DefaultPointcutAdvisor(Advice advice); 用的切面就是 Pointcut.TRUE，所以如果你要指定切面，请使用自己指定的构造函数
        // Pointcut.TRUE：表示啥都返回 true，也就是说这个切面作用于所有的方法上/所有的方法
        // addAdvice();方法最终内部都是被包装成一个 `DefaultPointcutAdvisor`，且使用的是 Pointcut.TRUE 切面，因此需要注意这些区别，相当于 new DefaultPointcutAdvisor(Pointcut.TRUE,advice);
        Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
        factory.addAdvisor(advisor);
        Person p = (Person) factory.getProxy();

        // 执行方法
        p.run();
        p.run(10);
        p.say();
        p.sayHi("Jack");
        p.say("Tom", 666);
    }

    // 不需要 Spring 容器，https://blog.csdn.net/f641385712/article/details/89178421
    @Test
    public void methodPointcutTest() {
        ProxyFactory factory = new ProxyFactory(new Person());
        // 声明一个 aspectj 切点，一张切面
        JdkRegexpMethodPointcut cut = new JdkRegexpMethodPointcut();
        // cut.setPattern("aop.PointcutTest.Person.run"); // 拦截 Person 类下所有 run 的方法（无法精确到方法签名）
        // cut.setPattern(".*run.*"); // .号匹配除"\r\n"之外的任何单个字符。*号代表零次或多次匹配前面的字符或子表达式，所以它拦截任意包下任意类的 run 方法
        cut.setPatterns(new String[]{".*run.*", ".*say.*"}); // 可以配置多个正则表达式，sayHi 方法也会被拦截

        // 声明一个通知（此处使用环绕通知 MethodInterceptor ）
        Advice advice = (MethodInterceptor) invocation -> {
            System.out.println("============>放行前拦截...");
            Object obj = invocation.proceed();
            System.out.println("============>放行后拦截...");
            return obj;
        };

        // 切面 = 切点 + 通知
        // 它还有个构造函数：DefaultPointcutAdvisor(Advice advice); 用的切面就是 Pointcut.TRUE，所以如果你要指定切面，请使用自己指定的构造函数
        // Pointcut.TRUE：表示啥都返回 true，也就是说这个切面作用于所有的方法上/所有的方法
        // addAdvice();方法最终内部都是被包装成一个 `DefaultPointcutAdvisor`，且使用的是 Pointcut.TRUE 切面，因此需要注意这些区别，相当于 new DefaultPointcutAdvisor(Pointcut.TRUE,advice);
        Advisor advisor = new DefaultPointcutAdvisor(cut, advice);
        factory.addAdvisor(advisor);
        Person p = (Person) factory.getProxy();

        // 执行方法
        p.run();
        p.run(10);
        p.say();
        p.sayHi("Jack");
        p.say("Tom", 666);
    }

    static class Person {
        public int run() {
            System.out.println("我在run...");
            return 0;
        }

        public void run(int i) {
            System.out.println("我在run...<" + i + ">");
        }

        public void say() {
            System.out.println("我在say...");
        }

        public void sayHi(String name) {
            System.out.println("Hi," + name + ",你好");
        }

        public int say(String name, int i) {
            System.out.println(name + "----" + i);
            return 0;
        }
    }
}
